using System;
using System.Collections.Generic;
using FireStarter.CodeGeneration.Generator;
using FireStarter.CodeGeneration.Parser;
using NUnit.Framework;

namespace FireStarter.Test.CodeGeneration.Parser
{
    [TestFixture]
    public class TestExpressionParser
    {
        private static readonly List<IActionParser> _defaultActionParsers = new List<IActionParser> { new ValueExpressionParser(), new ForEachExpressionParser() };
        
        //TODO: Cater for Comments: <<|-- This is a comment --|>>

        [Test]
        public void Test_EmptyExpression()
        {
            //---------------Set up test pack-------------------
            const string masterContent = "<<| \t \r\n  \t |>>";
            string content = masterContent;
            IParser parser = new ExpressionParser();

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);

            //---------------Test Result -----------------------
            ParserTestUtils.AssertExpressionIsLiteral("", expression);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ExceptionThrownForActionWithoutMatch()
        {
            //---------------Set up test pack-------------------
            string content = "<<|INVALIDACTION|>>";
            IParser parser = new ExpressionParser();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            try
            {
                TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
                Assert.Fail("Invalid action should have caused exception");
            }
            catch (ParserException ex)
            {
                string errorMessage = "The given action does not match the template language: INVALIDACTION" +
                                      Environment.NewLine + "(Note that a value replacement is preceded by = )";
                StringAssert.AreEqualIgnoringCase(errorMessage, ex.Message);
            }
            //---------------Test Result -----------------------

        }

        [Test]
        public void Test_ValueAction()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string content = string.Format("<<|={0}|>>", propertyName);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ParserTestUtils.AssertExpressionIsValue(propertyName, expression);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ValueAction_WithEndCharacters()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string suffix = TestUtilsCodeGen.GetRandomString();
            string content = string.Format("<<|={0}|>>{1}", propertyName, suffix);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ParserTestUtils.AssertExpressionIsValue(propertyName, expression);
            Assert.AreEqual(suffix, content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ForEachAction()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string innerContent = TestUtilsCodeGen.GetRandomString();
            string content = string.Format("<<|FOREACH({0}):{1}|>>", propertyName, innerContent);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ForEachExpression forEachExpression;
            ParserTestUtils.AssertExpressionIsForEach(propertyName, expression, out forEachExpression);
            ParserTestUtils.AssertExpressionIsLiteral(innerContent, forEachExpression.Part);
            Assert.IsNull(forEachExpression.Next);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ForEachAction_WithWhiteSpace_BeforeColon()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string innerContent = TestUtilsCodeGen.GetRandomString();
            string content = string.Format("<<|FOREACH({1}){0}:{2}|>>",
                TestUtilsCodeGen.GetRandomWhiteSpace(), propertyName, innerContent);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ForEachExpression forEachExpression;
            ParserTestUtils.AssertExpressionIsForEach(propertyName, expression, out forEachExpression);
            ParserTestUtils.AssertExpressionIsLiteral(innerContent, forEachExpression.Part);
            Assert.IsNull(forEachExpression.Next);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ForEachAction_WithWhiteSpace_AfterColon()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string innerContent = TestUtilsCodeGen.GetRandomWhiteSpace() + TestUtilsCodeGen.GetRandomString();
            string content = string.Format("<<|FOREACH({0}):{1}|>>", propertyName, innerContent);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ForEachExpression forEachExpression;
            ParserTestUtils.AssertExpressionIsForEach(propertyName, expression, out forEachExpression);
            ParserTestUtils.AssertExpressionIsLiteral(innerContent, forEachExpression.Part);
            Assert.IsNull(forEachExpression.Next);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ForEachAction_WithWhiteSpace_BeforeEnd()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string innerContent = TestUtilsCodeGen.GetRandomString() + TestUtilsCodeGen.GetRandomWhiteSpace();
            string content = string.Format("<<|FOREACH({0}):{1}|>>", propertyName, innerContent);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ForEachExpression forEachExpression;
            ParserTestUtils.AssertExpressionIsForEach(propertyName, expression, out forEachExpression);
            ParserTestUtils.AssertExpressionIsLiteral(innerContent, forEachExpression.Part);
            Assert.IsNull(forEachExpression.Next);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ForEachAction_WithInnerValueExpression()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string innerPropertyName = TestUtilsCodeGen.GetRandomString();
            string innerContent = string.Format("<<|={0}|>>", innerPropertyName);
            string content = string.Format("<<|FOREACH({0}):{1}|>>", propertyName, innerContent);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ForEachExpression forEachExpression;
            ParserTestUtils.AssertExpressionIsForEach(propertyName, expression, out forEachExpression);
            ParserTestUtils.AssertExpressionIsValue(innerPropertyName, forEachExpression.Part);
            Assert.IsNull(forEachExpression.Next);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_ForEachAction_WithInnerForEachExpression()
        {
            //---------------Set up test pack-------------------
            string propertyName = TestUtilsCodeGen.GetRandomString();
            string innerPropertyName = TestUtilsCodeGen.GetRandomString();
            string innerInnerLiteral = TestUtilsCodeGen.GetRandomString();
            string innerContent = string.Format("<<|FOREACH({0}):{1}|>>", innerPropertyName, innerInnerLiteral);
            string content = string.Format("<<|FOREACH({0}):{1}|>>", propertyName, innerContent);
            IParser parser = new ExpressionParser();
            //-------------Assert Preconditions -------------

            //---------------Execute Test ----------------------
            TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
            ForEachExpression forEachExpression;
            ParserTestUtils.AssertExpressionIsForEach(propertyName, expression, out forEachExpression);
            ForEachExpression innerForEachExpression;
            ParserTestUtils.AssertExpressionIsForEach(innerPropertyName, forEachExpression.Part, out innerForEachExpression);
            ParserTestUtils.AssertExpressionIsLiteral(innerInnerLiteral, innerForEachExpression.Part);
            Assert.AreEqual("", content, "Expression text should be consumed");
        }

        [Test]
        public void Test_NoClosingTagThrowsException()
        {
            //---------------Set up test pack-------------------
            string content = "<<|FOREACH(testvalue):";
            IParser parser = new ExpressionParser();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            try
            {
                TemplateExpression expression = parser.Parse(ref content, _defaultActionParsers);
            //---------------Test Result -----------------------
                Assert.Fail("ParserException should be thrown when closing tag not found");
            }
            catch (ParserException ex)
            {
                string expectedMessage = "A template had no closing tag: " + content;
                Assert.AreEqual(expectedMessage, ex.Message);
            }
        }

        

        //TODO: Tests for badly formed template Expressions, etc

        //private MemoryStream GetStreamForString(string content)
        //{
        //    MemoryStream contentStream = new MemoryStream();
        //    StreamWriter writer = new StreamWriter(contentStream);
        //    writer.Write(content);
        //    writer.Flush();
        //    contentStream.Seek(0, SeekOrigin.Begin);
        //    return contentStream;
        //}
    }
}